/**
 * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springblade.modules.huikecloud.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.AllArgsConstructor;
import org.springblade.common.constant.CommonConstant;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.modules.huikecloud.entity.Port;
import org.springblade.modules.huikecloud.mapper.PortMapper;
import org.springblade.modules.huikecloud.service.AsyncService;
import org.springblade.modules.huikecloud.service.IHostService;
import org.springblade.modules.huikecloud.service.IPortService;
import org.springblade.modules.huikecloud.utils.IPUtil;
import org.springblade.modules.huikecloud.utils.ScriptUtil;
import org.springblade.modules.huikecloud.vo.PortVO;
import org.springblade.modules.system.service.IParamService;
import org.springframework.stereotype.Service;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * 服务实现类
 *
 * @author Blade
 * @since 2021-07-08
 */
@Service
@AllArgsConstructor
public class PortServiceImpl extends BaseServiceImpl<PortMapper, Port> implements IPortService {

    private IParamService paramService;
    private IHostService hostService;
    private AsyncService asyncService;

    @Override
    public IPage<PortVO> selectPortPage(IPage<PortVO> page, PortVO port) {
        return page.setRecords(baseMapper.selectPortPage(page, port));
    }


    @Override
    public boolean submit(Port port) {
        if ("tcp".equals(port.getMappingType()) && CommonConstant.CLOUD_TCP_AUTO){
            saveOrUpdate(port);
            return addMapping(port);
        }else if ("web".equals(port.getMappingType()) && CommonConstant.CLOUD_WEB_AUTO){
            saveOrUpdate(port);
            return addMapping(port);
        }
        return saveOrUpdate(port);
    }


    //   添加映射
    public boolean addMapping(Port port) {


        String nginxUrl = CommonConstant.CLOUD_NGINX_URL;
        String script = "";
        String content = "端口已开通 \n";
        if (port != null && "web".equals(port.getMappingType())) {//添加web映射
            script = "cat > " + nginxUrl + "conf/vhosts/" + port.getMappingName() + ".conf  <<EOF\n" +
                    "server {\n" +
                    "    listen 80;\n" +
                    "    server_name " + port.getMappingUrl() + ";\n" +
                    "    access_log  " + nginxUrl + "logs/" + port.getMappingName() + ".access.log  main;\n" +
                    "    error_log  " + nginxUrl + "logs/" + port.getMappingName() + ".error.log;\n" +
                    "    location / {\n" +
                    "        add_header 'Access-Control-Allow-Origin' '*' always;\n" +
                    "        add_header 'Access-Control-Allow-Credentials' 'true' always;\n" +
                    "        add_header 'Access-Control-Allow-Methods' 'OPTION, POST, GET' always;\n" +
                    "        add_header 'Access-Control-Allow-Headers' 'X-Requested-With, Content-Type, Authorization, Accept' always;\n" +
                    "        proxy_pass              http://" + port.getIp() + ":" + port.getPort() + ";\n" +
                    "        proxy_set_header        Host            \\$host;\n" +
                    "        proxy_set_header        X-Real-IP       \\$remote_addr;\n" +
                    "        proxy_set_header        X-Forwarded-For \\$proxy_add_x_forwarded_for;\n" +
                    "    }\n" +
                    "}\n" +
                    "EOF\n";
//            port.setMappingUrl(port.getMappingName());

            content = content +
                    port.getIp() + ":" + port.getPort() + "   映射到 "+port.getMappingUrl() + "\n"
                    + "请在本地hosts 添加 \n"
                    + CommonConstant.CLOUD_PROXY_IP + "  " + port.getMappingUrl() + "\n" +
                    "后访问 " + port.getMappingUrl() + "\n" +
                    "系统通知，请勿回复！";
        } else if (port != null && "tcp".equals(port.getMappingType())) {//添加tcp映射

            Integer mappingPort = CommonConstant.CLOUD_TCP_STARTPORT;
            while (IPUtil.isLocalPortUsing(mappingPort)) {
                ++mappingPort;
                if (mappingPort >= 15000) {
                    mappingPort = 10000;
                }
            }
            script = "cat > " + nginxUrl + "conf/vhosts/" + port.getMappingName() + ".conf <<EOF\n" +
                    "server {\n" +
                    "    listen " + mappingPort + ";\n" +
//					"    access_log  " + nginxUrl + "logs/" + port.getMappingName() + ".access.log  main;\n" +
//					"    error_log  " + nginxUrl + "logs/" + port.getMappingName() + ".error.log;\n" +
                    "    proxy_connect_timeout 10s;\n" +
                    "    proxy_timeout 30s;\n" +
                    "    proxy_pass " + port.getIp() + ":" + port.getPort() + ";   \n" +
                    "}\n" +
                    "EOF\n";
            port.setMappingUrl(CommonConstant.CLOUD_PROXY_IP+":"+mappingPort);

            content = content +
                    port.getIp() + ":" + port.getPort() + "   映射到 "+port.getMappingUrl() + "\n" +
                    "系统通知，请勿回复！";
            CommonConstant.CLOUD_TCP_STARTPORT=mappingPort;
        }
        boolean addFlag = ScriptUtil.runShell(script, "新增脚本");
        boolean updateFlag = ScriptUtil.runShell(nginxUrl + "sbin/nginx -s reload", "更新配置");
        if (addFlag && updateFlag) {

//            port.setScript(JSON.toJSONString(script));
            port.setScript(script);
            port.setStatus(1);
            baseMapper.updateById(port);
            paramService.saveParam();
//          邮件通知学生
            asyncService.sendUserMail(port.getUserEmail(), port.getMappingName(), content);
//          邮件通知管理员
            asyncService.sendAdminMail(port.getMappingName(), "新增映射成功\n"+content);
            return true;
        } else {
            asyncService.sendAdminMail(port.getMappingName(), "新增映射失败，请检查并重试\n"+content);
            return false;
        }
    }


    //   删除映射
    public boolean delMapping(Port port) {
        hostService.mapping(port,-1);
        String nginxUrl = CommonConstant.CLOUD_NGINX_URL;
        boolean addFlag = ScriptUtil.runShell("rm -rf " + nginxUrl + "conf/vhosts/" + port.getMappingName() + ".conf", "删除脚本");
        boolean updateFlag = ScriptUtil.runShell(nginxUrl + "sbin/nginx -s reload", "更新配置");

        baseMapper.deleteById(port.getId());
        if (addFlag && updateFlag){
            return true;
        }
        asyncService.sendAdminMail(port.getMappingName(), "删除映射失败，请检查并重试");
        return false;
    }



    /**
     * 通过所有待审核的映射
     */
    public boolean passAll() {
        QueryWrapper<Port> queryWrapper = new QueryWrapper<Port>();
        List<Port> portList = list(queryWrapper.lambda().eq(Port::getStatus, 0));
        for (Port port : portList) {
            addMapping(port);
        }
        return true;
    }

    /**
     * 删除过期的映射
     */
    public boolean delMapping() {
        QueryWrapper<Port> queryWrapper = new QueryWrapper<Port>();
        Calendar c = Calendar.getInstance();
        c.add(Calendar.MONTH, -4);
        Date monday = c.getTime();
        List<Port> portList = list(queryWrapper.lambda().ne(Port::getUserName,"管理员").lt(Port::getCreateTime,monday));
        for (Port port : portList) {
            delMapping(port);
        }
        return true;
    }
}
